#include "lib/helpers/pvminstreamhelpers.h"
#include "lib/pvmsystem.h"
#include "lib/pvmstrings.h"

#include "lib/memory/pvmmemory.h"

PVMAPI IPvmInStream* PVMCALL PVMHCreateMemoryInputStream(pvm_int8* dataPtr, pvm_size dataSize, pvm_bool ownMemory, TFreeMemmoryProc memFreeProc)
{
	return PVMHMemmoryInputStream::Create(dataPtr, dataSize, ownMemory, memFreeProc);
}

PVMAPI IPvmInStream* PVMCALL PVMHCreateFileInputStream(const pvm_char* fileName)
{
	return PVMHFileInputStream::CreateW(fileName);
}

PVMAPI void PVMCALL PVMHCloseStream(IPvmBaseStream* stream)
{
	stream->Close();
}

/************************************************************************/
/* PVMHRefMemObject                                                     */
/************************************************************************/

PVMHRefMemObject::PVMHRefMemObject(pvm_int8* data, pvm_size size, pvm_bool ownMemory, TFreeMemmoryProc memFreeProc)
{
	this->data = data;
	this->dataSize = size;
	this->ownMemory = ownMemory;
	this->refCount = 0;
	this->memFreeProc = memFreeProc;
}

PVMHRefMemObject::~PVMHRefMemObject()
{
	if(ownMemory)
	{
		if(memFreeProc!=NULL)
			memFreeProc(data);
		else
			delete[](data);
	}

	data = 0;
	dataSize = 0;
	ownMemory = false;
}

pvm_int PVMHRefMemObject::AddRef()
{
	pvm_int n = ++refCount;
	return n;
}

pvm_int PVMHRefMemObject::Release()
{
	pvm_int n = refCount;
	if(refCount>0)
	{
		n = --refCount;
		if(refCount<=0)
		{
			delete( this);
		}
	}
	return n;
}

/************************************************************************/
/* PVMHMemmoryInputStream                                               */
/************************************************************************/

PVMHMemmoryInputStream::PVMHMemmoryInputStream()
{
	memObj = NULL;
	position = 0;
}

PVMHMemmoryInputStream::~PVMHMemmoryInputStream()
{
	if(memObj)
		memObj->Release();
	memObj = NULL;
	position = 0;
}

IPvmInStream* PVMHMemmoryInputStream::Create(pvm_int8* dataPtr, pvm_size dataSize, pvm_bool ownMemory, TFreeMemmoryProc memFreeProc)
{
	PVMHMemmoryInputStream* stream = new PVMHMemmoryInputStream();
	if(!stream)
		return NULL;

	PVMHRefMemObject* memObject = new PVMHRefMemObject(dataPtr, dataSize, ownMemory, memFreeProc);
	if(!memObject)
	{
		delete( stream);
		return NULL;
	}

	stream->memObj = memObject;
	stream->memObj->AddRef();

	return stream;
}

pvm_size PVMHMemmoryInputStream::Read(void *buf, pvm_size size)
{
	pvm_size bytesToCopy = size;
	if(((pvm_file_size)bytesToCopy)>memObj->Size()-position)
		bytesToCopy = (pvm_size)(memObj->Size() - position);

	if(position<0||position>=Size())
		return 0;

	memcpy(buf, ((char*)memObj->Data()) + position, bytesToCopy);
	position += bytesToCopy;

	return bytesToCopy;
}

pvm_bool PVMHMemmoryInputStream::Seek(pvm_file_size pos, TPvmSeek origin)
{
	pvm_file_size newPosition;
	switch(origin)
	{
		case EPVM_SEEK_BEGIN:
			newPosition = pos;
			break;
		case EPVM_SEEK_CURRENT:
			newPosition = position + pos;
			break;
		case EPVM_SEEK_END:
			newPosition = Size() + pos;
			break;
		default: return false;
	};

	if(newPosition<0)
		newPosition = 0;

	if(newPosition>Size())
		newPosition = Size() - 1;

	position = newPosition;

	return true;
}

pvm_file_size PVMHMemmoryInputStream::Tell()
{
	return position;
}

void PVMHMemmoryInputStream::Close()
{
	delete( this);
}

pvm_file_size PVMHMemmoryInputStream::Size()
{
	return memObj->Size();
}

IPvmInStream* PVMHMemmoryInputStream::OpenCopy()
{
	PVMHMemmoryInputStream *stream = new PVMHMemmoryInputStream();
	if(!stream)
		return NULL;

	stream->memObj = memObj;
	stream->memObj->AddRef();

	return stream;
}

pvm_bool PVMHMemmoryInputStream::Seekable()
{
	return true;
}

pvm_bool PVMHMemmoryInputStream::Eof()
{
	return (memObj->Size() - position)<=0;
}


/************************************************************************/
/* PVMHFileInputStream                                                  */
/************************************************************************/

PVMHFileInputStream::PVMHFileInputStream()
{
	name = NULL;
	file = PVM_EMPTY_FILE_HANDLE;
	eof = false;
}

PVMHFileInputStream::~PVMHFileInputStream()
{
	if(name)
		delete[](name);

	if(file!=PVM_EMPTY_FILE_HANDLE)
		PVMFileClose(file);

	name = NULL;
	file = PVM_EMPTY_FILE_HANDLE;
}

IPvmInStream* PVMHFileInputStream::CreateW(const pvm_char* fileName)
{
	PVMHFileInputStream* stream = new PVMHFileInputStream();
	if(!stream)
		return NULL;


	PVMFileHandle f = PVMFileOpen(fileName, PVM_READ | PVM_OPEN_EXISTING);
	if(f==PVM_FILE_OPEN_ERROR)
	{
		delete( stream);
		return NULL;
	}

	stream->file = f;

	pvm_size strLen = pvm_strlen(fileName);
	pvm_char *newStr = (pvm_char*)PVMMemoryAlloc((strLen+1)*sizeof(pvm_char));
	if(!newStr)
		return stream;
	pvm_strcpy(newStr, fileName);
	stream->name = newStr;

	return stream;
}

pvm_size PVMHFileInputStream::Read(void *buf, pvm_size size)
{
	pvm_size result = 0;
	if(PVMFileRead(file, buf, size, &result)==false && result==0)
		eof = true;
	return result;
}

pvm_bool PVMHFileInputStream::Seek(pvm_file_size pos, TPvmSeek origin)
{
	return PVMFileSeek(file, pos, origin);
}

pvm_file_size PVMHFileInputStream::Tell()
{
	return PVMFileTell(file);
}

void PVMHFileInputStream::Close()
{
	delete( this);
}

pvm_file_size PVMHFileInputStream::Size()
{
	return PVMFileSize(file);
}

IPvmInStream* PVMHFileInputStream::OpenCopy()
{
	if(!name)
		return NULL;

	return CreateW(name);
}

pvm_bool PVMHFileInputStream::Seekable()
{
	return true;
}

pvm_bool PVMHFileInputStream::Eof()
{
	return eof;
}


/************************************************************************/
/* PVMInCallbacksStream                                                 */
/************************************************************************/

PVMInCallbacksStream::PVMInCallbacksStream(IPVMReadCallbacks* callbacks, void *src)
{
	this->callbacks = callbacks;
	this->src = src;
}

PVMInCallbacksStream::~PVMInCallbacksStream()
{
	callbacks = NULL;
	src = NULL;
}

pvm_size PVMInCallbacksStream::Read(void *buf, pvm_size size)
{
	return callbacks->Read(src, buf, size);
}
pvm_bool PVMInCallbacksStream::Seek(pvm_file_size pos, TPvmSeek origin)
{
	return callbacks->Seek(src, pos, origin);
}

pvm_file_size PVMInCallbacksStream::Tell()
{
	return callbacks->Tell(src);
}

void PVMInCallbacksStream::Close()
{
	return callbacks->Close(src);
}

pvm_file_size PVMInCallbacksStream::Size()
{
	return callbacks->Size(src);
}

IPvmInStream* PVMInCallbacksStream::OpenCopy()
{
	void* newSrc = callbacks->OpenCopy(src);
	if(!newSrc)
		return NULL;
	PVMInCallbacksStream* newStream = new PVMInCallbacksStream(callbacks, newSrc);
	if(!newStream)
	{
		callbacks->Close(newSrc);
	}
	return newStream;
}

pvm_bool PVMInCallbacksStream::Seekable()
{
	return callbacks->Seekable(src);
}

pvm_bool PVMInCallbacksStream::Eof()
{
	return callbacks->Eof(src);
}

/************************************************************************/
/* PVMHCreateFileInputStreamCallbacks                                   */
/************************************************************************/


pvm_size Read(void *src, void *buf, pvm_size size)
{
	pvm_size readResult = 0;
	PVMFileRead((PVMFileHandle)((IPVMReadCallbacks*)src)->param, buf, size, &readResult);

	return readResult;
}

pvm_bool Seek(void *src, pvm_file_size pos, int origin)
{
	return PVMFileSeek((PVMFileHandle)((IPVMReadCallbacks*)src)->param, pos, origin);
}

pvm_file_size Tell(void *src)
{
	return PVMFileTell((PVMFileHandle)((IPVMReadCallbacks*)src)->param);
}

void Close(void *src)
{
	PVMFileClose((PVMFileHandle)((IPVMReadCallbacks*)src)->param);
	PVMMemoryFree(src);
}

pvm_file_size Size(void *src)
{
	return PVMFileSize((PVMFileHandle)((IPVMReadCallbacks*)src)->param);
}
/*
void* OpenCopy(void *src)
{
	return NULL;
}
*/
pvm_bool Seekable(void *src)
{
	return true;
}

pvm_bool Eof(void *src)
{
	//return PVMFileEof((PVMFileHandle)((IPVMReadCallbacks*)src)->param);
	return false;
}
